import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 公交站点数据
stations_data = pd.DataFrame({
    'Station_ID': [1, 2, 3, 4, 5, 6, 7, 8, 9],
    'Longitude': [110.125713, 110.08442, 110.029866, 109.962839, 109.956003, 109.920425, 109.839046, 109.823329,
                  109.767127],
    'Latitude': [32.815024, 32.771676, 32.748994, 32.743622, 32.812194, 32.856136, 32.860495, 32.847468, 32.807855]
})

# 需求点数据
demands_data = pd.DataFrame({
    'Demand_ID': range(1, 51),
    'Longitude': [
        110.1053385, 110.1147032, 110.0862574, 110.0435344, 110.0575508,
        110.0386243, 110.0115086, 110.0390602, 110.0246454, 110.0575847,
        109.9456331, 109.9612274, 109.94592, 109.9316682, 109.9245376,
        109.7087533, 109.7748005, 109.7475891, 109.7534532, 109.783015,
        109.7410728, 109.7554844, 109.7147417, 109.8807093, 109.8070677,
        109.9054481, 109.8954509, 109.8979229, 109.8942179, 109.8610985,
        109.8744682, 109.8338804, 109.870924, 109.8292467, 109.8711312,
        109.8813363, 109.978788, 109.8166563, 109.8151216, 109.885638,
        109.9890984, 109.9647812, 109.9303732, 109.9401099, 109.944496,
        109.979708, 109.976757, 109.94999, 109.973673, 109.967765
    ],
    'Latitude': [
        32.77881526, 32.75599834, 32.74905239, 32.74275416, 32.76712584,
        32.70855831, 32.72619993, 32.73965997, 32.72360718, 32.76553658,
        32.7526657, 32.72286471, 32.70899877, 32.73848444, 32.70740885,
        32.7815564, 32.80016336, 32.80903496, 32.85129032, 32.82296929,
        32.82914197, 32.80581363, 32.79995734, 32.89696579, 32.79622985,
        32.89437141, 32.86724756, 32.83444574, 32.83224374, 32.90687042,
        32.89939698, 32.85616627, 32.848223, 32.83825122, 32.88979101,
        32.8642824, 32.75943454, 32.8096699, 32.82822489, 32.84032485,
        32.80854774, 32.80993619, 32.78956582, 32.85264625, 32.802178,
        32.817449, 32.811064, 32.795207, 32.746858, 32.820998
    ],
    'Delivery_kg': [3, 4, 2, 0, 8, 7, 4, 9, 10, 6, 7, 12, 3, 5, 6, 5, 3, 13, 12, 3,
                    14, 10, 4, 34, 6, 6, 3, 4, 20, 5, 6, 5, 3, 15, 2, 6, 3, 4, 3, 2,
                    6, 5, 9, 3, 3, 4, 6, 4, 4, 0]
})

# 无人机参数
drone_juti = {
    'A': {'D_max': 27, 'Q_max': 9, 'C_fixed': 80, 'C_per_km': 0.8},
    'B': {'D_max': 16, 'Q_max': 30, 'C_fixed': 150, 'C_per_km': 1.5},
    'C': {'D_max': 18, 'Q_max': 50, 'C_fixed': 200, 'C_per_km': 2.5}
}

# 公交车参数
bus_aver_speed = 35  # 公交车速度（km/h）
# 发车时间表
bus_regaschedule = {
    '白河至仓上': [6.67, 8.5, 9, 11, 14, 16.5],
    '仓上至白河': [6, 7.33, 8.83, 11, 14, 15.83]
}


# 计算两个点之间的距离（calculate_distince公式）
def calculate_distince(lon1, lat1, lon2, lat2):
    lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2])
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
    c = 2 * np.arcsin(np.sqrt(a))
    r = 6371  # 地球平均半径（公里）
    return c * r


# 将公交站点和需求点的经纬度转换为NumPy数组
stations_lons = stations_data['Longitude'].values
stations_lats = stations_data['Latitude'].values
demands_lons = demands_data['Longitude'].values
demands_lats = demands_data['Latitude'].values

# 利用广播机制计算所有公交站点到所有需求点的距离
stations_lons_matrix, demands_lons_matrix = np.meshgrid(stations_lons, demands_lons)
stations_lats_matrix, demands_lats_matrix = np.meshgrid(stations_lats, demands_lats)
distances_matrix = calculate_distince(stations_lons_matrix, stations_lats_matrix, demands_lons_matrix, demands_lats_matrix)

# 将距离矩阵转换为字典形式
distances = {(stations_data['Station_ID'][i], demands_data['Demand_ID'][j]): distances_matrix[j, i]
             for i in range(len(stations_data)) for j in range(len(demands_data))}


# 问题2：三种类型无人机的最小费用协同配送方案，考虑重复使用
def allocate_tasks_costs_multi_drone():
    total_cost = 0
    assigned_tasks = []
    demand_visited = set()
    station_drone_usage = {station_id: {'A': 0, 'B': 0, 'C': 0} for station_id in stations_data['Station_ID']}

    for demand_id, demand in demands_data.iterrows():
        if demand_id in demand_visited:
            continue

        best_cost = float('inf')
        best_station = None
        best_drone = None

        for drone_type, params in drone_juti.items():
            D_max = params['D_max']
            Q_max = params['Q_max']
            C_fixed = params['C_fixed']
            C_per_km = params['C_per_km']

            for station_id, station in stations_data.iterrows():
                station_id = station['Station_ID']
                distance = distances[(station_id, demand['Demand_ID'])]
                if distance <= D_max and demand['Delivery_kg'] <= Q_max:
                    cost = C_fixed if station_drone_usage[station_id][drone_type] == 0 else 0
                    cost += 2 * distance * C_per_km  # 往返距离
                    if cost < best_cost:
                        best_cost = cost
                        best_station = station_id
                        best_drone = drone_type

        if best_station is not None and best_drone is not None:
            total_cost += best_cost
            assigned_tasks.append((demand['Demand_ID'], best_station, best_cost, best_drone))
            station_drone_usage[best_station][best_drone] += 1
            demand_visited.add(demand_id)

    return total_cost, assigned_tasks


total_cost_multi, task_allocation_multi = allocate_tasks_costs_multi_drone()


def generate_flight_schedule(assigned_tasks):
    wait_time = 5 / 60  # 等待时间（小时）
    flight_schedule = []

    for direction, times in bus_regaschedule.items():
        for time in times:
            for task in assigned_tasks:
                demand_id, station_id, cost, drone_type = task
                arrival_time = time
                distance = distances[(station_id, demand_id)]
                flight_time = distance / (bus_aver_speed / 2)
                start_time = arrival_time + wait_time
                end_time = start_time + flight_time

                flight_schedule.append({
                    'Demand_ID': demand_id,
                    'Drone_Type': drone_type,
                    'Station_ID': station_id,
                    'Flight_Start_Time': start_time,
                    'Flight_End_Time': end_time
                })

    return flight_schedule


flight_schedule_multi = generate_flight_schedule(task_allocation_multi)


# 任务分配信息
def print_assigned_tasks(assigned_tasks):
    allocation_by_station = {}
    for task in assigned_tasks:
        demand_id, station_id, cost, drone_type = task
        if station_id not in allocation_by_station:
            allocation_by_station[station_id] = {}
        if drone_type not in allocation_by_station[station_id]:
            allocation_by_station[station_id][drone_type] = []
        allocation_by_station[station_id][drone_type].append(demand_id)

    print("无人机任务分配:")
    for station_id, drones in allocation_by_station.items():
        for drone_type, demands in drones.items():
            print(f"  公交站点 {station_id}: 需求点 {demands} -> 无人机类型 {drone_type}")

# 可视化飞行路径
def visualize_flight_paths(assigned_tasks, title):
    fig, ax = plt.subplots()

    # 绘制公交站点
    ax.scatter(stations_data['Longitude'], stations_data['Latitude'], c='red', label='Bus Stations')

    # 绘制需求点
    ax.scatter(demands_data['Longitude'], demands_data['Latitude'], c='blue', label='Demand Points')

    # 绘制飞行路径
    for task in assigned_tasks:
        demand_id, station_id, cost, drone_type = task
        demand = demands_data.loc[demands_data['Demand_ID'] == demand_id].iloc[0]
        station = stations_data.loc[stations_data['Station_ID'] == station_id].iloc[0]
        ax.plot([station['Longitude'], demand['Longitude']], [station['Latitude'], demand['Latitude']],
                label=f' {drone_type}')

    ax.legend()
    ax.set_xlabel('Longitude')
    ax.set_ylabel('Latitude')
    ax.set_title(title)
    plt.show()


# 可视化问题2的飞行路径
visualize_flight_paths(task_allocation_multi, 'Flight Paths - Problem 2 (Multiple drone types)')

# 打印任务分配
print_assigned_tasks(task_allocation_multi)


print(f"问题2最小总费用：{total_cost_multi:.0f} 元")

